// Fill out your copyright notice in the Description page of Project Settings.


#include "SnakeBase.h"
#include "SnakeElementBase.h"
#include "Engine/Classes/Components/StaticMeshComponent.h"
#include "Interactable.h"
#include "SnakeGameModeBase.h"


// Sets default values
ASnakeBase::ASnakeBase()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	ElementSize = 50.0f;
	TickSpeed = 0.5f;
	CurrentTickSpeed = TickSpeed;
	SpeedupTime = 0;
	LastMoveDirection = EMovementDirection::UP;
	CompleatedStep = true;
	FoodEaten = 0;
	SpeedupStepDuration = 50;	
	CurrentPoints = 0;
}

// Called when the game starts or when spawned
void ASnakeBase::BeginPlay()
{
	Super::BeginPlay();
	ElementSize = Cast<ASnakeGameModeBase>(GetWorld()->GetAuthGameMode())->GetElementSize();
	SetActorLocation(FVector(-ElementSize * 0.5f, -ElementSize * 0.5f, 0.0f));
	SetActorTickInterval(TickSpeed);
	AddSnakeElement(5, GetActorLocation());
	CurrentPoints = 0;
	SnakeElements[0]->SetFirstElementType();
}

// Called every frame
void ASnakeBase::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	Move();
	CompleatedStep = true;
	SlowDown();
}

void ASnakeBase::AddSnakeElement(int ElementsCount, FVector InitialLocation)
{
	float Proportion = (ElementSize - 1.0f) * 0.01f;
	for (int i = 0; i < ElementsCount; i++)
	{
		FTransform NewElementTransform = FTransform(InitialLocation - FVector(i * ElementSize, 0.0f, 0.0f));
		ASnakeElementBase* NewElement = GetWorld()->SpawnActor<ASnakeElementBase>(SnakeElementClass, NewElementTransform);
		NewElement->MeshComponent->SetWorldScale3D(FVector(Proportion, Proportion, Proportion));
		NewElement->SnakeOwner = this;
		SnakeElements.Add(NewElement);
		if (SpeedupTime > 0)
			NewElement->SetSpeedupState(true);
		CurrentPoints += 1000 * (SnakeElements.Num() - 5);
	}
}

void ASnakeBase::Move()
{
	FVector MovementVector = FVector().ZeroVector;
	switch (LastMoveDirection)
	{
	case EMovementDirection::UP:
		MovementVector.X += ElementSize;
		break;
	case EMovementDirection::DOWN:
		MovementVector.X -= ElementSize;
		break;
	case EMovementDirection::LEFT:
		MovementVector.Y += ElementSize;
		break;
	case EMovementDirection::RIGHT:
		MovementVector.Y -= ElementSize;
		break;
	default:
		break;
	}
	SnakeElements[0]->ToggleCollision();
	FVector LastElement = SnakeElements.Last()->GetActorLocation();
	for (int i = SnakeElements.Num() - 1; i > 0; i--)
	{
		ASnakeElementBase* CurrentElement = SnakeElements[i];
		ASnakeElementBase* PreviousElement = SnakeElements[i - 1];
		FVector PreviousLocation = PreviousElement->GetActorLocation();
		CurrentElement->SetActorLocation(PreviousLocation);
	}
	if (FoodEaten > 0)
	{
		AddSnakeElement(1, LastElement);
		FoodEaten--;
	}
	SnakeElements[0]->AddActorWorldOffset(MovementVector);
	SnakeElements[0]->ToggleCollision();
}

void ASnakeBase::SnakeElementOverlap(ASnakeElementBase* OverlappedElement, AActor* Other)
{
	if (!IsValid(OverlappedElement)) return;
	bool IsHead = SnakeElements[0] == OverlappedElement;
	IInteractable* InteractableInterface = Cast<IInteractable>(Other);
	if (InteractableInterface)
	{
		InteractableInterface->Interact(this, IsHead);
	}
}

void ASnakeBase::SlowDown()
{
	if (SpeedupTime == 0)
	{
		for (int i = SnakeElements.Num() - 1; i > 0; i--)
		{
			ASnakeElementBase* CurrentElement = SnakeElements[i];
			CurrentElement->SetSpeedupState(false);
		}
		return;
	}
	SpeedupTime--;
	if (SpeedupTime % SpeedupStepDuration != 0) return;
	SetActorTickInterval(CurrentTickSpeed);
	CurrentTickSpeed += TickSpeed * 0.25;
}

void ASnakeBase::SpeedUpActivate()
{
	for (int i = SnakeElements.Num() - 1; i > 0; i--)
	{
		ASnakeElementBase* CurrentElement = SnakeElements[i];
		CurrentElement->SetSpeedupState(true);
	}
	CurrentTickSpeed = TickSpeed * 0.25;
	SetActorTickInterval(CurrentTickSpeed);
	SpeedupTime = 3 * SpeedupStepDuration;
	CurrentPoints += 2500 * SnakeElements.Num();
}

void ASnakeBase::Death()
{
	SnakeElements[0]->Destroy();
	Cast<ASnakeGameModeBase>(GetWorld()->GetAuthGameMode())->FixDeath();
	Destroy();
}

